/// ------------------------------------------------------------------------------------------------------------------------------------
///
/// MIT License
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all
/// copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
/// SOFTWARE.
///
/// Copyright (c) 2022 ycz. All rights reserved.
///
/// Created by ycz on 2022/1/2.
///
/// @file  y_protocol.c
///
/// @brief
///     y_protocol 具体功能实现
///
/// ------------------------------------------------------------------------------------------------------------------------------------



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 头文件
/// ------------------------------------------------------------------------------------------------------------------------------------

#include "y_protocol.h"

#include <stdlib.h>
#include <string.h>
#include "y_sa.h"



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 宏定义
/// ------------------------------------------------------------------------------------------------------------------------------------

#ifdef _Y_LL_H
#    define Y_MALLOC y_ll_malloc
#    define Y_FREE   y_ll_free
#else
#    define Y_MALLOC malloc
#    define Y_FREE   free
#endif



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 私有函数
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   获取 sum8 校验值
/// @param   [in] data                     要处理数据的指针
/// @param   [in] size                     要处理数据的大小
/// @return  sum8 校验值
static uint8_t _y_protocol_get_sum8(uint8_t *data, uint16_t size) {

    uint8_t sum = 0;
    while (size--) {
        sum += *(data + size);  // 求和
    }
    return ~sum;  // 取反
}

/// @brief   加/解密数据
/// @param   [in]     encrypt_type         加/解密类型
/// @param   [in]     is_encrypt           加密还是解密
/// @param   [in,out] data                 要处理数据的指针
/// @param   [in]     size                 要处理数据的大小
/// @retval  true                          成功
/// @retval  false                         失败
static bool _y_protocol_encrypt_decrypt(PROTOCOL_ENCRYPT_e encrypt_type, bool is_encrypt, uint8_t *data, uint16_t size) {

    // 断言
    YLOGA_FALSE(data);
    YLOGA_FALSE(size);

    // 根据对应的方式进行加密
    switch (encrypt_type) {
        case ENCRYPT_NONE:  // 无加密
            break;

        case ENCRYPT_CAESAR_3: {  // 凯撒加密 (字符偏移3 A->D B->E 以此类推)
            for (int i = 0; i < size; ++i) {
                if (is_encrypt) {
                    data[i] = data[i] + 3;  // 加密数据
                } else {
                    data[i] = data[i] - 3;  // 解密数据
                }
            }
        } break;

        case ENCRYPT_XOR: {  // 异或加密
            for (int i = 0; i < size; ++i) {
                data[i] = data[i] ^ 0X4A;
            }
        } break;

        case ENCRYPT_MIXED: {  // 混合加密 (先凯撒，在异或)
            for (int i = 0; i < size; ++i) {
                if (is_encrypt) {
                    data[i] = data[i] + 3;     // 凯撒 加密数据
                    data[i] = data[i] ^ 0X4A;  // 异或 加密数据
                } else {
                    data[i] = data[i] ^ 0X4A;  // 异或 解密数据
                    data[i] = data[i] - 3;     // 凯撒 解密数据
                }
            }
        } break;

        default:
            YLOGW("unsupported encrypt");
            return false;
    }

    return true;
}

/// @brief   获取 mod 类型字符串
/// @param   [in] mod                      mod 类型
/// @return  字符串
char *_y_protocol_get_mod_str(PROTOCOL_MOD_e mod) {

    switch (mod) {
        case MOD_LINK:
            return "LINK";
        case MOD_SYS:
            return "SYS";
        case MOD_FACTORY:
            return "FACTORY";
        case MOD_OTA:
            return "OTA";
        case MOD_DEBUG:
            return "DEBUG";
        case MOD_INTER:
            return "INTER";
        case MOD_BUSINESS:
            return "BUSINESS";
        case MOD_PROPERTY:
            return "PROPERTY";
        case MOD_EVENT:
            return "EVENT";
        case MOD_FUNCTION:
            return "FUNCTION";
        default:
            return "UNKNOWN";
    }
}



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 公有函数
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   打印 y_protocol 版本信息
void y_protocol_print_version() {
    YLOG_VERSION("y_protocol", Y_PROTOCOL_MAJOR, Y_PROTOCOL_MINOR, Y_PROTOCOL_PATCH);
}

/// @brief   打包协议
/// @param   [in]  msg                     协议结构体
/// @param   [out] pack_data               打包好的数据指针
/// @param   [out] pack_size               打包好的数据大小
/// @retval  true                          成功
/// @retval  false                         失败
bool y_protocol_pack(void *msg, uint8_t **pack_data, uint16_t *pack_size) {

    // 断言
    YLOGA_FALSE(msg);
    YLOGA_FALSE(pack_data);
    YLOGA_FALSE(pack_size);

    // 获取协议头
    PROTOCOL_HEAD_st *head = msg;

    // 根据协议类型 准备打包后的内容
    switch (head->type) {

        case PROTOCOL_O2O_V1: {  // 一对一 V1 协议类型
            // 开辟空间
            PROTOCOL_O2O_V1_st *p_msg = (PROTOCOL_O2O_V1_st *) msg;
            *pack_size                = sizeof(PROTOCOL_O2O_V1_st) - sizeof(p_msg->cmd_data) + p_msg->cmd_size + 1;
            *pack_data                = (uint8_t *) Y_MALLOC(*pack_size);  // 开辟 pack_data 空间 = 结构体大小 - 数据指针大小 + cmd数据大小 + 校验值大小
            YLOGA_FALSE(*pack_data);
            // 获取消息体大小
            p_msg->head.size = sizeof(PROTOCOL_O2O_V1_st) - sizeof(PROTOCOL_HEAD_st) - sizeof(p_msg->cmd_data) + p_msg->cmd_size;
            // 填充数据到 pack_data
            memcpy(*pack_data, p_msg, sizeof(PROTOCOL_O2O_V1_st) - sizeof(p_msg->cmd_data));  // 填充结构体中的数据 不包含数据指针
            if (p_msg->cmd_size) {
                if (p_msg->cmd_data == NULL) {
                    YLOGE("cmd_size !=0 , but cmd_data is NULL");
                    Y_FREE(*pack_data);
                    *pack_data = NULL;
                    *pack_size = 0;
                    return false;
                }
                memcpy(*pack_data + sizeof(PROTOCOL_O2O_V1_st) - sizeof(p_msg->cmd_data), p_msg->cmd_data, p_msg->cmd_size);  // 填充数据指针中的数据
            }
        } break;

        case PROTOCOL_M2M_V1: {  // 多对多 V1 协议类型
            // 开辟空间
            PROTOCOL_M2M_V1_st *p_msg = (PROTOCOL_M2M_V1_st *) msg;
            *pack_size                = sizeof(PROTOCOL_M2M_V1_st) - sizeof(p_msg->cmd_data) + p_msg->cmd_size + 1;
            *pack_data                = (uint8_t *) Y_MALLOC(*pack_size);  // 开辟 pack_data 空间 = 结构体大小 - 数据指针大小 + cmd数据大小 + 校验值大小
            YLOGA_FALSE(*pack_data);
            // 获取消息体大小
            p_msg->head.size = sizeof(PROTOCOL_M2M_V1_st) - sizeof(PROTOCOL_HEAD_st) - sizeof(p_msg->cmd_data) + p_msg->cmd_size;
            // 填充数据到 pack_data
            memcpy(*pack_data, p_msg, sizeof(PROTOCOL_M2M_V1_st) - sizeof(p_msg->cmd_data));  // 填充结构体中的数据 不包含数据指针
            if (p_msg->cmd_size) {
                if (p_msg->cmd_data == NULL) {
                    YLOGE("cmd_size !=0 , but cmd_data is NULL");
                    Y_FREE(*pack_data);
                    *pack_data = NULL;
                    *pack_size = 0;
                    return false;
                }
                memcpy(*pack_data + sizeof(PROTOCOL_M2M_V1_st) - sizeof(p_msg->cmd_data), p_msg->cmd_data, p_msg->cmd_size);  // 填充数据指针中的数据
            }
        } break;

        default:
            YLOGW("unsupported pack type");
            return false;
    }

    // 添加累加校验和
    (*pack_data)[(*pack_size) - 1] = _y_protocol_get_sum8(*pack_data, (*pack_size) - 1);

    // 加密数据
    if (_y_protocol_encrypt_decrypt(head->encrypt, true, *pack_data + sizeof(PROTOCOL_HEAD_st), head->size + 1) == false) {
        YLOGE("encrypt data error");
        Y_FREE(*pack_data);
        *pack_data = NULL;
        *pack_size = 0;
        return false;
    }

    return true;
}

/// @brief   解包协议
/// @param   [in]  unpack_data             解包数据指针
/// @param   [in]  unpack_size             解包数据大小
/// @param   [out] msg                     解包后的协议格式结构体
/// @retval  true                          成功
/// @retval  false                         失败
bool y_protocol_unpack(uint8_t *unpack_data, uint16_t unpack_size, void **msg) {

    // 断言
    YLOGA_FALSE(unpack_data);
    YLOGA_FALSE(unpack_size > sizeof(PROTOCOL_HEAD_st));
    YLOGA_FALSE(msg);

    // 获取协议头
    PROTOCOL_HEAD_st *head = (PROTOCOL_HEAD_st *) unpack_data;
    if (unpack_size != head->size + sizeof(PROTOCOL_HEAD_st) + 1 || head->size == 0) {
        return false;  // 数据不够 或 没有消息数据
    }

    // 解密数据
    if (_y_protocol_encrypt_decrypt(head->encrypt, false, unpack_data + sizeof(PROTOCOL_HEAD_st), head->size + 1) == false) {
        YLOGE("decrypt data error");
        return false;
    }

    // 检测累加校验和
    if (_y_protocol_get_sum8(unpack_data, unpack_size - 1) != unpack_data[unpack_size - 1]) {
        YLOGD("unpack_data check sum fail");
        return false;
    }

    // 根据协议类型 解析协议内容
    switch (head->type) {

        case PROTOCOL_O2O_V1: {  // 一对一 V1 协议类型
            // 开辟协议结构体空间
            PROTOCOL_O2O_V1_st *p_msg = (PROTOCOL_O2O_V1_st *) Y_MALLOC(sizeof(PROTOCOL_O2O_V1_st));
            YLOGA_FALSE(p_msg);
            // 填充数据到结构体
            uint16_t tmp_size = sizeof(PROTOCOL_O2O_V1_st) - sizeof(p_msg->cmd_data);  // 协议除指针部分占用大小
            memcpy(p_msg, unpack_data, tmp_size);
            if (p_msg->cmd_size != (unpack_size - tmp_size - 1)) {
                YLOGE("cmd_size error");
                Y_FREE(p_msg);
                return false;
            }
            if (p_msg->cmd_size) {
                p_msg->cmd_data = (uint8_t *) Y_MALLOC(p_msg->cmd_size);
                if (p_msg->cmd_data == NULL) {
                    YLOGE("Y_MALLOC cmd_data fail");
                    Y_FREE(p_msg);
                    return false;
                }
                memcpy(p_msg->cmd_data, unpack_data + tmp_size, p_msg->cmd_size);
            }
            // 将解包好的协议数据带出去
            *msg = p_msg;
        } break;

        case PROTOCOL_M2M_V1: {  // 多对多 V1 协议类型
            // 开辟协议结构体空间
            PROTOCOL_M2M_V1_st *p_msg = (PROTOCOL_M2M_V1_st *) Y_MALLOC(sizeof(PROTOCOL_M2M_V1_st));
            YLOGA_FALSE(p_msg);
            // 填充数据到结构体
            uint16_t tmp_size = sizeof(PROTOCOL_M2M_V1_st) - sizeof(p_msg->cmd_data);  // 协议除指针部分占用大小
            memcpy(p_msg, unpack_data, tmp_size);
            if (p_msg->cmd_size != (unpack_size - tmp_size - 1)) {
                YLOGE("cmd_size error");
                Y_FREE(p_msg);
                return false;
            }
            if (p_msg->cmd_size) {
                p_msg->cmd_data = (uint8_t *) Y_MALLOC(p_msg->cmd_size);
                if (p_msg->cmd_data == NULL) {
                    Y_FREE(p_msg);
                    YLOGE("Y_MALLOC cmd_data fail");
                    return false;
                }
                memcpy(p_msg->cmd_data, unpack_data + tmp_size, p_msg->cmd_size);
            }
            // 将解包好的协议数据带出去
            *msg = p_msg;
        } break;

        default:
            YLOGW("unsupported unpack type");
            return false;
    }

    return true;
}

/// @brief   删除协议消息
/// @param   [in] msg                      协议格式结构体
/// @retval  true                          成功
/// @retval  false                         失败
bool y_protocol_delete(void *msg) {

    // 断言
    YLOGA_FALSE(msg);

    // 获取协议头
    PROTOCOL_HEAD_st *head = (PROTOCOL_HEAD_st *) msg;

    // 根据类型删除消息
    switch (head->type) {

        case PROTOCOL_O2O_V1: {  // 一对一 V1 协议类型
            PROTOCOL_O2O_V1_st *p_msg = (PROTOCOL_O2O_V1_st *) msg;
            if (p_msg->cmd_data && p_msg->cmd_size) {
                Y_FREE(p_msg->cmd_data);
            }
            Y_FREE(p_msg);
            return true;
        }

        case PROTOCOL_M2M_V1: {  // 多对多 V1 协议类型
            PROTOCOL_M2M_V1_st *p_msg = (PROTOCOL_M2M_V1_st *) msg;
            if (p_msg->cmd_data && p_msg->cmd_size) {
                Y_FREE(p_msg->cmd_data);
            }
            Y_FREE(p_msg);
            return true;
        }

        default:
            return false;
    }
}

/// @brief   转存消息 (使用 Y_MALLOC 创建, 使用后需要释放)
/// @param   [in] msg                      协议格式结构体
/// @return  转存消息
void *y_protocol_dump(void *msg) {

    // 断言
    YLOGA_NULL(msg);

    // 获取协议头
    PROTOCOL_HEAD_st *head = (PROTOCOL_HEAD_st *) msg;

    // 根据协议类型 转存协议内容
    switch (head->type) {

        case PROTOCOL_O2O_V1: {  // 一对一 V1 协议类型
            // 开辟空间
            PROTOCOL_O2O_V1_st *p_msg = (PROTOCOL_O2O_V1_st *) Y_MALLOC(sizeof(PROTOCOL_O2O_V1_st));
            YLOGA_NULL(p_msg);
            memcpy(p_msg, msg, sizeof(PROTOCOL_O2O_V1_st));
            if (p_msg->cmd_size) {
                p_msg->cmd_data = (uint8_t *) Y_MALLOC(p_msg->cmd_size);
                if (p_msg->cmd_data == NULL) {
                    YLOGE("Y_MALLOC cmd_data fail");
                    Y_FREE(p_msg);
                    return NULL;
                }
                memcpy(p_msg->cmd_data, ((PROTOCOL_O2O_V1_st *) msg)->cmd_data, p_msg->cmd_size);
            }
            return p_msg;
        }

        case PROTOCOL_M2M_V1: {  // 多对多 V1 协议类型
            // 开辟空间
            PROTOCOL_M2M_V1_st *p_msg = (PROTOCOL_M2M_V1_st *) Y_MALLOC(sizeof(PROTOCOL_M2M_V1_st));
            YLOGA_NULL(p_msg);
            memcpy(p_msg, msg, sizeof(PROTOCOL_M2M_V1_st));
            if (p_msg->cmd_size) {
                p_msg->cmd_data = (uint8_t *) Y_MALLOC(p_msg->cmd_size);
                if (p_msg->cmd_data == NULL) {
                    YLOGE("Y_MALLOC cmd_data fail");
                    Y_FREE(p_msg);
                    return NULL;
                }
                memcpy(p_msg->cmd_data, ((PROTOCOL_M2M_V1_st *) msg)->cmd_data, p_msg->cmd_size);
            }
            return p_msg;
        }

        default:
            YLOGW("unsupported dump type");
            return NULL;
    }
}

/// @brief   获取设备类型字符串
/// @param   [in] dev_type                 设备类型
/// @return  字符串
__attribute__((weak)) char *y_protocol_get_dev_str(uint8_t dev_type) {

    // todo：在外部重写此函数
    switch (dev_type) {
        default:
            return "UNKNOWN";
    }
}

/// @brief   打印消息
/// @param   [in] msg                      消息指针
void y_protocol_print_m2m_msg(PROTOCOL_M2M_V1_st *msg) {

    uint8_t str[6] = {0};
    if (y_utils_cmp_mac6(y_sys_get_mac(), msg->recv_mac)) {
        strcpy((char *) str, "RECV");
    } else if (y_utils_cmp_mac6(y_sys_get_mac(), msg->send_mac)) {
        strcpy((char *) str, "SEND");
    }

    // 断言
    YLOGA(msg);
    YLOGI("———————————————————————————————————————————————————————————————————————————————————————————————————————");
    YLOGI("////   %4s :  %3d %18.18s #%-3d   %8s - %-3d | " MAC_FORMAT " -> " MAC_FORMAT " | %3d   ////",
          str,
          msg->head.num,
          y_protocol_get_dev_str(msg->dev_type),
          msg->dev_id,
          _y_protocol_get_mod_str(msg->mod_id),
          msg->cmd_id,
          MAC_TO_STR(msg->send_mac),
          MAC_TO_STR(msg->recv_mac),
          msg->cmd_size);
    YLOGI("———————————————————————————————————————————————————————————————————————————————————————————————————————");
}

/// @brief   通信服务 创建消息
/// @param   [in] num                      消息编号 (为 0 则由服务自动生成)
/// @param   [in] send_mac                 发送者 mac 地址
/// @param   [in] recv_mac                 接收者 mac 地址（NULL 表示发送到父节点）
/// @param   [in] dev_type                 设备类型
/// @param   [in] dev_id                   设备编号
/// @param   [in] wan_id                   网络编号
/// @param   [in] mod_id                   模块 id
/// @param   [in] cmd_id                   命令 id
/// @param   [in] cmd_data                 命令数据指针
/// @param   [in] cmd_size                 命令数据大小
/// @return  消息结构体
PROTOCOL_M2M_V1_st y_protocol_create_m2m_msg(uint8_t  num,
                                             uint8_t *send_mac,
                                             uint8_t *recv_mac,
                                             uint8_t  dev_type,
                                             uint16_t dev_id,
                                             uint8_t  wan_id,
                                             uint8_t  mod_id,
                                             uint8_t  cmd_id,
                                             uint8_t *cmd_data,
                                             uint16_t cmd_size) {

    PROTOCOL_M2M_V1_st msg = {0};
    msg.head.type          = PROTOCOL_M2M_V1;  // 协议类型
    msg.head.encrypt       = ENCRYPT_NONE;     // 加密方式
    msg.head.num           = num;              // 更新消息编号
    msg.dev_type           = dev_type;         // 设备类型
    msg.dev_id             = dev_id;           // 设备编号
    msg.wan_id             = wan_id;           // 网络 id
    msg.mod_id             = mod_id;           // 模块 id
    msg.cmd_id             = cmd_id;           // 命令 id
    msg.cmd_size           = cmd_size;         // 命令数据指针
    msg.cmd_data           = cmd_data;         // 命令数据大小
    if (send_mac) {
        memcpy(msg.send_mac, send_mac, sizeof(msg.send_mac));
    }
    if (recv_mac) {
        memcpy(msg.recv_mac, recv_mac, sizeof(msg.recv_mac));
    }
    // 修正参数
    if (num == 0) {
        static uint8_t last_num = 0;
        msg.head.num            = (++last_num == 0) ? ++last_num : last_num;  // 更新消息编号 ( 跳过 0 )
    }

    return msg;
}
